﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace _3D_Game
{
    class ParticleExplosion
    {
        VertexPositionTexture[] verts;
        Vector3[] vertexDirectionArray;
        Color[] vertexColorArray;
        VertexBuffer particleVertexBuffer;

        Vector3 position;

        int lifeLeft;

        int numParticlesPerRound;
        int maxParticles;
        static Random random = new Random();
        int roundTime;
        int timeSinceLastRound;

        GraphicsDevice graphicsDevice;

        ParticleSettings particleSettings;

        Effect particleEffect;

        Texture2D particleColorsTexture;

        int endOfLiveParticlesIndex = 0;
        int endOfDeadParticlesIndex = 0;

        public ParticleExplosion(GraphicsDevice graphicsDevice, Vector3 position, int lifeLeft, int roundTime, int numParticlesPerRound, int maxParticles,
            Texture2D particleColorsTexture, ParticleSettings particleSettings, Effect particleEffect)
        {
            this.position = position;
            this.lifeLeft = lifeLeft;
            this.numParticlesPerRound = numParticlesPerRound;
            this.maxParticles = maxParticles;
            this.roundTime = roundTime;
            this.graphicsDevice = graphicsDevice;
            this.particleSettings = particleSettings;
            this.particleEffect = particleEffect;
            this.particleColorsTexture = particleColorsTexture;

            InitializeParticleVertices();
        }

        private void InitializeParticleVertices()
        {
            verts = new VertexPositionTexture[maxParticles*4];
            vertexDirectionArray = new Vector3[maxParticles];
            vertexColorArray = new Color[maxParticles];

            Color[] colors = new Color [particleColorsTexture.Width * particleColorsTexture.Height];
            particleColorsTexture.GetData(colors);

            for(int i=0; i<maxParticles;++i)
            {
                float size = (float)random.NextDouble() * particleSettings.maxSize;

                verts[i*4] = new VertexPositionTexture(position, new Vector2(0,0));
                verts[(i*4+1)] = new VertexPositionTexture(new Vector3(position.X, position.Y + size, position.Z), new Vector2(0,1));
                verts[(i*4+2)] = new VertexPositionTexture(new Vector3(position.X + size, position.Y, position.Z), new Vector2(1,0));
                verts[(i*4+3)] = new VertexPositionTexture(new Vector3(position.X+size, position.Y+size, position.Z), new Vector2(1,1));

                Vector3 direction = new Vector3((float)random.NextDouble() *2-1, (float)random.NextDouble() * 2-1, (float)random.NextDouble() * 2-1);
                direction.Normalize();

                //make sure that the particles move at different speeds;
                direction *= (float)random.NextDouble();

                vertexDirectionArray[i] = direction;

                //set color of Particle by getting a random color from texture
                vertexColorArray[i] = colors[(random.Next(0, particleColorsTexture.Height) * particleColorsTexture.Width) + 
                    random.Next(0, particleColorsTexture.Width)];
            }
            
            particleVertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionTexture), verts.Length, BufferUsage.None);
        }

        public void Update(GameTime gameTime)
        {
            if (lifeLeft > 0)

                lifeLeft -= gameTime.ElapsedGameTime.Milliseconds;

            timeSinceLastRound += gameTime.ElapsedGameTime.Milliseconds;
            if (timeSinceLastRound > roundTime)
            {
                timeSinceLastRound -= roundTime;

                //Increment end of live particles index each round until end of list is reached
                if (endOfLiveParticlesIndex < maxParticles)
                {
                    endOfLiveParticlesIndex += numParticlesPerRound;
                    if (endOfLiveParticlesIndex > maxParticles)
                        endOfLiveParticlesIndex = maxParticles;
                }

                if(lifeLeft <= 0)
                {
                    //increment end of dead particles index each round until end of list i reached
                    endOfDeadParticlesIndex += numParticlesPerRound;
                    if(endOfDeadParticlesIndex > maxParticles)
                        endOfDeadParticlesIndex = maxParticles;
                }
            }

            //Update positions of all live particles
            for (int i = endOfDeadParticlesIndex; i < endOfLiveParticlesIndex; ++i)
            {
                verts[i * 4].Position += vertexDirectionArray[i];
                verts[(i * 4 + 1)].Position += vertexDirectionArray[i];
                verts[(i * 4 + 2)].Position += vertexDirectionArray[i];
                verts[(i * 4 + 3)].Position += vertexDirectionArray[i];
            }
        }

        public void Draw(Camera camera)
        {
            graphicsDevice.SetVertexBuffer(particleVertexBuffer);

            if (endOfLiveParticlesIndex - endOfDeadParticlesIndex > 0)
            {
                for (int i = 0; i < endOfLiveParticlesIndex; ++i)
                {
                    particleEffect.Parameters["WorldViewProjection"].SetValue(camera.view * camera.projection);
                    particleEffect.Parameters["particleColor"].SetValue(vertexColorArray[i].ToVector4());

                    foreach (EffectPass pass in particleEffect.CurrentTechnique.Passes)
                    {
                        pass.Apply();

                        graphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleStrip, verts, i * 4, 2);
                    }
                }
            }
        }


        public bool IsDead
        {
            get { return endOfDeadParticlesIndex == maxParticles; }
        }

    }
}
